/*
 * Copyright (c) 2004-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gou Pengfei
 *          Jin Yinghan
 */

#include <algorithm>

#include "arch/types.hh"
#include "base/trace.hh"
#include "base/traceflags.hh"
#include "cpu/edge/bpred_unit.hh"
#include "params/DerivEdgeCPU.hh"

template<class Impl>
EdgeBPredUnit<Impl>::EdgeBPredUnit(DerivEdgeCPUParams *params)
    : _name(params->name + ".EdgeBPredUnit"),
      historyTracker(params),
      defaultBTB(params->BTBEntries,
          params->offsetLength,
          params->blockShiftAmt),
      iBTB(params->BTBEntries,
           params->blockShiftAmt,
           params->iBTBIndexType),
      blockShiftAmt(params->blockShiftAmt)
{
    // Setup the selected exitPredictor.
    exitPredictor = Unknown;

    if (params->exitPredType == "Perfect") {

        exitPredictor = Perf;

        perceptronBP = NULL;
        tournamentBP = NULL;
        tournamentPerceptron = NULL;
        piecewiseExitP = NULL;

    } else if (params->exitPredType == "TargetOnly") {

        exitPredictor = TargetOnly;

        perceptronBP = NULL;
        tournamentBP = NULL;
        tournamentPerceptron = NULL;
        piecewiseExitP = NULL;

    } else if (params->exitPredType == "Perceptron") {

        perceptronBP = new PerceptronBP<Impl>(params,0);

        exitPredictor = Percep;

    } else if (params->exitPredType == "Tournament") {

        tournamentBP =
            new TournamentBP<Impl>(params->localPredictorSize,
                                   params->localHistoryTableSize,
                                   params->localHistoryBits,
                                   params->globalPredictorSize,
                                   params->globalHistoryBits,
                                   params->choiceHistoryBits,
                                   params->choicePredictorSize,
                                   params->choiceCtrBits,
                                   params->blockShiftAmt);
        exitPredictor = Tour;
    } else if (params->exitPredType == "TournamentPerceptron") {

        tournamentPerceptron = new TournamentPerceptron<Impl>(params);

        exitPredictor = TourPercep;

    } else if (params->exitPredType == "PiecewiseExitP") {

        piecewiseExitP = new PiecewiseExitP<Impl>(params);

        exitPredictor = Piecewise;

    } else if (params->exitPredType == "Global") {
        globalExitBP = new GlobalExitP<Impl>(params,
                params->bexitGlobalIndexMode);

        exitPredictor = Global;

    } else if (params->exitPredType == "Local") {

        localExitBP = new LocalExitP<Impl>(params);

        exitPredictor = Local;

    } else if (params->exitPredType == "NewTour") {

        newTourExitBP = new TourFrameworkExit<Impl>(params);

        exitPredictor = NewTour;

    } else if (params->exitPredType == "TAGE") {

        tageExitBP = new TAGEExitP<Impl>(params);

        exitPredictor = TAGE;

    } else {

        DPRINTF(EdgePredUnit,"%s.\n",params->exitPredType);

        fatal("Invalid BP selected!\n");
    }

    // Setup the branch type predictor.
    btypePredictor = Unknown;

    if (params->btypePredType == "Perfect") {
        twostageBtypeBP = NULL;
        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        newTourBtypeBP = NULL;
        tournamentBtypeBP = NULL;
        tageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = Perf;

        if (!params->preExecuteMode) {
            fatal("Perfect type predictor requires preExecuteMode enable.\n");
        }

    } else if (params->btypePredType == "PerfectTypeOnly") {
        twostageBtypeBP = NULL;
        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        newTourBtypeBP = NULL;
        tournamentBtypeBP = NULL;
        tageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = PerfTypeOnly;

        if (!params->preExecuteMode) {
            fatal("Perfect type predictor requires preExecuteMode enable.\n");
        }

    } else if (params->btypePredType == "Hysteresis") {
        hysteresisBtypeBP = new HysteresisTypeP<Impl>(params);
        hysteresisBtypeBP->init(params->BTPEntries,
                                params->blockShiftAmt,
                                params->hystBTypeIndexMode,
                                params->hystBTypeTableMode,
                                false);

        piecewiseBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        tournamentBtypeBP = NULL;
        newTourBtypeBP = NULL;
        tageBtypeBP = NULL;
        twostageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = Hysteresis;

    } else if (params->btypePredType == "Piecewise") {
        piecewiseBtypeBP = new PiecewiseBP<Impl>(params);
        piecewiseBtypeBP->initMode(PiecewiseBP<Impl>::SingleBitHistory);

        hysteresisBtypeBP = new HysteresisTypeP<Impl>(params);
        hysteresisBtypeBP->init(params->BTPEntries,
                                params->blockShiftAmt,
                                params->hystBTypeIndexMode,
                                params->hystBTypeTableMode,
                                true);

        btypePredictor = Piecewise;

        piecewiseBStypeBP = NULL;
        tournamentBtypeBP = NULL;
        newTourBtypeBP = NULL;
        tageBtypeBP = NULL;
        twostageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

    } else if (params->btypePredType == "PiecewiseSType") {
        piecewiseBStypeBP = new PiecewiseTypeP<Impl>(params);

        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        tournamentBtypeBP = NULL;
        newTourBtypeBP = NULL;
        tageBtypeBP = NULL;
        twostageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = PiecewiseSType;

    } else if (params->btypePredType == "Tournament") {

        tournamentBtypeBP = new TournamentTypeP<Impl>(params);
        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        newTourBtypeBP = NULL;
        tageBtypeBP = NULL;
        twostageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = Tour;

    } else if (params->btypePredType == "NewTour") {
        newTourBtypeBP = new TournamentFramework<Impl>(params);

        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        tournamentBtypeBP = NULL;
        tageBtypeBP = NULL;
        twostageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = NewTour;

    } else if (params->btypePredType == "Global") {
        globalBtypeBP = new GlobalTypeP<Impl>(params,
                params->btypeGlobalIndexMode);

        newTourBtypeBP = NULL;
        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        tournamentBtypeBP = NULL;
        tageBtypeBP = NULL;
        twostageBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = Global;

    } else if (params->btypePredType == "Local") {
        localBtypeBP = new LocalTypeP<Impl>(params);

        newTourBtypeBP = NULL;
        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        tournamentBtypeBP = NULL;
        tageBtypeBP = NULL;
        twostageBtypeBP = NULL;
        globalBtypeBP = NULL;

        btypePredictor = Local;

    } else if (params->btypePredType == "TAGE") {

        tageBtypeBP = new TAGETypeP<Impl>(params);

        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        newTourBtypeBP = NULL;
        tournamentBtypeBP = NULL;
        twostageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = TAGE;

    } else if (params->btypePredType == "Twostage") {

        twostageBtypeBP = new TwostageFramework<Impl>(params);

        piecewiseBtypeBP = NULL;
        hysteresisBtypeBP = NULL;
        piecewiseBStypeBP = NULL;
        newTourBtypeBP = NULL;
        tournamentBtypeBP = NULL;
        tageBtypeBP = NULL;
        globalBtypeBP = NULL;
        localBtypeBP = NULL;

        btypePredictor = Twostage;

    } else {
        fatal("Invalid branch type predictor selected!\n");
    }

    // Setup the btb type
    btbType = BTBUnknown;

    if (params->btbType == "BTB") {
        fatal("Deprecated btb type selected!\n");
        btbType = NormalBTB;
    } else if (params->btbType == "iBTB") {
        fatal("Deprecated btb type selected!\n");
        btbType = IndirectBTB;
    } else if (params->btbType == "NewBTB") {
        btbType = NewBTB;
        newBTB = new BTB<Impl>(params);
    } else {
        fatal("Invalid btb type selected!\n");
    }

    CTB.init(params->CTBEntries, params->blockShiftAmt, params->offsetLength);
    
    RAS.init(params->RASSize, params->lsEntries, params->blockShiftAmt);
}

template<class Impl>
EdgeBPredUnit<Impl>::~EdgeBPredUnit()
{
    DPRINTF(EdgePredUnit, "EdgeBPredUnit destructed.\n");
}

template <class Impl>
void
EdgeBPredUnit<Impl>::regStats()
{
    lookups
        .name(name() + ".lookups")
        .desc("Number of BP lookups")
        ;

    condPredicted
        .name(name() + ".condPredicted")
        .desc("Number of conditional branches predicted")
        ;

    condIncorrect
        .name(name() + ".condIncorrect")
        .desc("Number of conditional branches incorrect")
        ;

    BTBLookups
        .name(name() + ".BTBLookups")
        .desc("Number of BTB lookups")
        ;

    BTBHits
        .name(name() + ".BTBHits")
        .desc("Number of BTB hits")
        ;

    BTBCorrect
        .name(name() + ".BTBCorrect")
        .desc("Number of correct BTB predictions (this stat may not "
              "work properly.")
        ;

    usedRAS
        .name(name() + ".usedRAS")
        .desc("Number of times the RAS was used to get a target.")
        ;

    RASIncorrect
        .name(name() + ".RASInCorrect")
        .desc("Number of incorrect RAS predictions.")
        ;        

    if (exitPredictor == NewTour) {
        newTourExitBP->regStats();
    } else if (exitPredictor == TAGE) {
        tageExitBP->regStats();
    }

    if (btypePredictor == TAGE) {
        tageBtypeBP->regStats(); 
    } else if (btypePredictor == NewTour) {
        newTourBtypeBP->regStats();
    } else if (btypePredictor == Twostage) {
        twostageBtypeBP->regStats();
    }
}

template <class Impl>
void
EdgeBPredUnit<Impl>::switchOut()
{
    // Clear any state upon switch out.
    for (int i = 0; i < Impl::MaxThreads; ++i) {
        squash(0, i);
    }
}

template <class Impl>
void
EdgeBPredUnit<Impl>::takeOverFrom()
{
    panic("Unimplemented func: takeOverFrom()\n");
}

template <class Impl>
TheISA::ExitID
EdgeBPredUnit<Impl>::predict(BlockID oldest_block_id, BlockPtr &block, Addr &pred_target,
                             ExitType &exit_type, int addr_space_id,
                             ThreadID tid)
{
    // See if branch exitPredictor predicts taken.
    // If so, get its target addr either from the BTB or the RAS.
    // Save off record of branch stuff so the RAS can be fixed
    // up once it's done.

    using TheISA::MachInst;

    ExitID pred_exit_id = 0;
    ExitType pred_branch_type = TheISA::InvalidExitType;
    Addr target = pred_target;
    Addr seq_target;
    Addr btb_target;
    Addr ctb_target;
    Addr ras_target;
    Addr ras_entry;
    uint32_t ret_addr;
    ++lookups;

    BlockID block_id = block->getBlockID();
    Addr block_pc = block->getStartPC();

    // If the exit exitPredictor is ignored, we will get exit id 0 for
    // each block.
    pred_exit_id = BPLookup(block_id,
                            oldest_block_id,
                            block_pc,
                            addr_space_id,
                            tid);

    // If we have a target-only predictor, we get perfect exit predictions.
    if (exitPredictor == TargetOnly) {
        pred_exit_id = block->getPredExitID();

        // Sometimes either blocks in the wrong path or blocks with
        // faults could reach here with InvalidExitType/ID. As a result,
        // we need to set an arbitrary value to the type. This 
        // is expected not to affect the final results, since these
        // blocks will be squashed in the commit stage.
        if ((pred_exit_id >= TheISA::MaxExitsInBlock) || (pred_exit_id < 0)) {
            pred_exit_id = 0;
        }
    }

    pred_branch_type = BTypeLookup(block,
                                   block_id,
                                   oldest_block_id,
                                   block_pc,
                                   pred_exit_id,
                                   addr_space_id,
                                   tid,
                                   block->getTestInstPath()
                                   );

    if (btypePredictor == PerfTypeOnly) {
        pred_branch_type = block->getPredExitType();

        // Sometimes either blocks in the wrong path or blocks with
        // faults could reach here with InvalidExitType/ID. As a result,
        // we need to set an arbitrary value to the type. This 
        // is expected not to affect the final results, since these
        // blocks will be squashed in the commit stage.
        if (pred_branch_type == TheISA::InvalidExitType) {
            pred_branch_type = TheISA::branch;
        }
    }

    // For perfect type predictor case, both exit id and type are got from
    // pre-executed result.
    if (btypePredictor == Perf) {
        pred_branch_type = block->getPredExitType();
        pred_exit_id = block->getPredExitID();

        // Sometimes either blocks in the wrong path or blocks with
        // faults could reach here with InvalidExitType/ID. As a result,
        // we need to set an arbitrary value to the type. This 
        // is expected not to affect the final results, since these
        // blocks will be squashed in the commit stage.
        if (pred_branch_type == TheISA::InvalidExitType) {
            pred_branch_type = TheISA::branch;
        }

        if ((pred_exit_id >= TheISA::MaxExitsInBlock) || (pred_exit_id < 0)) {
            pred_exit_id = 0;
        }
    }

    DPRINTF(EdgePredUnit,"BranchPred: [tid:%i]: Branch exitPredictor predicted %i "
            "for PC %#x, [Bid:%i]\n",
            tid,
            pred_exit_id,
            block->getStartPC(),
            block->getBlockID());
 
    assert(pred_exit_id >= 0 && pred_exit_id < TheISA::MaxExitsInBlock);
  
    exit_type = pred_branch_type;

    DPRINTF(EdgePredUnit, "BranchPred: get [Type:%i]\n", pred_branch_type);

    if (exitPredictor != Perf) {
        // Now update the history tracker.
        historyTracker.lookup(block_pc, block_id, pred_exit_id, pred_branch_type, 
                block->getTestInstPath(), tid);

#ifndef NDEBUG
        DPRINTF(HistoryTracker, "Test inst path for [Bid:%lli] is %#x",
                block_id, block->getTestInstPath());

        // Checking histories 
        if (exitPredictor == TAGE) {
            uint64_t predictor_hist = tageExitBP->getGlobalHistory(block_id, tid);
            uint64_t predictor_path = tageExitBP->getPathHistory(block_id, tid);
            uint64_t tracker_hist = historyTracker.getExitHistory(block_id, tid);
            uint64_t tracker_path = historyTracker.getPathHistory(block_id, tid);

            DPRINTF(HistoryTracker, "Checking exit history:"
                    " from predictor: %#x, from tracker: %#x\n",
                    predictor_hist,
                    tracker_hist);

            DPRINTF(HistoryTracker, "Checking path history:"
                    " from predictor: %#x, from tracker: %#x\n",
                    predictor_path,
                    tracker_path);

            assert(predictor_hist == tracker_hist);
            assert(predictor_path == tracker_path);
        }

        if (btypePredictor == TAGE) {
            uint64_t predictor_hist = tageBtypeBP->getGlobalHistory(block_id, tid);
            uint64_t predictor_path = tageBtypeBP->getPathHistory(block_id, tid);
            uint64_t tracker_hist = historyTracker.getTypeHistory(block_id, tid);
            uint64_t tracker_path = historyTracker.getPathHistory(block_id, tid);

            DPRINTF(HistoryTracker, "Checking type history:"
                    " from predictor: %#x, from tracker: %#x\n",
                    predictor_hist,
                    tracker_hist);

            DPRINTF(HistoryTracker, "Checking path history:"
                    " from predictor: %#x, from tracker: %#x\n",
                    predictor_path,
                    tracker_path);

            assert(predictor_hist == tracker_hist);
            assert(predictor_path == tracker_path);
        }

        if (btypePredictor == Twostage) {
            if (!twostageBtypeBP->isPerfectSeq()) {
                uint64_t predictor_hist =
                    twostageBtypeBP->getGlobalHistory(block_id, tid);
                uint64_t predictor_path =
                    twostageBtypeBP->getPathHistory(block_id, tid);
                uint64_t tracker_hist = 
                    historyTracker.getBinaryHistory(block_id, tid);
                uint64_t tracker_path =
                    historyTracker.getPathHistory(block_id, tid);

                DPRINTF(HistoryTracker, "Checking binary history:"
                        " from predictor: %#x, from tracker: %#x\n",
                        predictor_hist,
                        tracker_hist);

                DPRINTF(HistoryTracker, "Checking path history:"
                        " from predictor: %#x, from tracker: %#x\n",
                        predictor_path,
                        tracker_path);

                assert(predictor_hist == tracker_hist);
                assert(predictor_path == tracker_path);
            }
        }
#endif
    }

    // Now lookup in the BTB, RAS or CTB.
    seq_target = block->getNextBlockPC();

    if (btbType == NormalBTB) {

        DPRINTF(EdgePredUnit, "Lookup normal btb.\n");
        btb_target = defaultBTB.lookup(block_pc, addr_space_id, pred_exit_id);

    } else if (btbType == IndirectBTB) {

        DPRINTF(EdgePredUnit, "Lookup indirect btb.\n");

        uint32_t hist = getGlobalHistory(block_id, tid);
        uint32_t path_hist = getPathHistory(block_id, tid);

        DPRINTF(EdgePredUnit, "Get history %#x.\n", hist);
        DPRINTF(EdgePredUnit, "Get path history %#x.\n", path_hist);

        btb_target = iBTB.lookup(block_pc, hist, path_hist);

    } else if (btbType == NewBTB) {
        DPRINTF(EdgePredUnit, "Lookup new btb.\n");

        // @TODO : now provide speculative histories to btb.  
        std::vector<uint64_t> hist;
        if (exitPredictor != Perf) {
            hist.resize(5);
            hist[BTB<Impl>::ExitHistory] = historyTracker.getExitHistory(tid);
            hist[BTB<Impl>::TypeHistory] = historyTracker.getTypeHistory(tid);
            hist[BTB<Impl>::BinaryHistory] = historyTracker.getBinaryHistory(tid);
            hist[BTB<Impl>::PathHistory] = historyTracker.getPathHistory(tid);
            hist[BTB<Impl>::PredicationHistory] = historyTracker.getPredicationPattern(block_id ,tid);
        } else {
            hist.resize(5, 0);
        }

        btb_target = newBTB->lookup(block_pc, hist);

    } else {
        panic("Invalid btb type selected!\n");
    }

    ctb_target = CTB.lookupTarget(block_pc, addr_space_id, pred_exit_id);
    ret_addr = CTB.lookupRetAddr(block_pc, addr_space_id, pred_exit_id);

    ras_entry = RAS.lookup(block_id, tid);
    ras_target = RAS.targetGen(ras_entry);

    if (pred_branch_type == TheISA::seq) {
        DPRINTF(EdgePredUnit,"BranchPred: [tid:%i][bid:%i]: ExitType is Seq.\n",
                tid,
                block->getBlockID());
        target = seq_target;
    } else if (pred_branch_type == TheISA::branch) {
        DPRINTF(EdgePredUnit,"BranchPred: [tid:%i][bid:%i]: ExitType is Branch.\n",
                tid,
                block->getBlockID());
        target = btb_target;
    } else if (pred_branch_type == TheISA::call) {
        DPRINTF(EdgePredUnit,"BranchPred: [tid:%i][bid:%i]: ExitType is Call."
                "Return address is %#x.\n",
                tid,
                block->getBlockID(),
                ret_addr<<7);
        target = ctb_target;
        RAS.push(ret_addr, tid);
    } else if (pred_branch_type == TheISA::ret) {
        DPRINTF(EdgePredUnit,"BranchPred: [tid:%i][bid:%i]: ExitType is Return.\n",
                tid,
                block->getBlockID());
        target = ras_target;
        RAS.pop(tid);
    } else{
        panic("Unknown branch type!");
    } 

    pred_target = target;

    DPRINTF(EdgePredUnit,"Block [PC:%#x]:Predicted target is %#x. \n",
            block_pc,
            target);

    return pred_exit_id;
}

template <class Impl>
void
EdgeBPredUnit<Impl>::update(const BlockID done_sn, ThreadID tid,
                            Addr block_pc,
                            int addr_space_id,
                            const Addr corr_target,
                            ExitType actually_branch_type,
                            ExitType pred_branch_type,
                            bool seq_mispredicted,
                            ExitID actually_exit_id,
                            ExitID pred_exit_id,
                            bool exit_mispredicted)
{
    DPRINTF(EdgePredUnit, "BranchPred: [tid:%i]: Committing branches until "
            "[sn:%lli].\n block_pc = %#x, ExitID = %i and target = %#x.\n", 
            tid, done_sn, block_pc,
            actually_exit_id,
            corr_target);

    // Only squash the exit predictor when exit is mispredicted.
    //if (exit_mispredicted) {
    //    DPRINTF(EdgePredUnit, "BranchPred: [tid:%i]: Squashing the exit"
    //            " predictor because exit id is mispredicted. "
    //            "actual:%i, pred:%i\n",
    //            tid, actually_exit_id, pred_exit_id);

    // **ATTENTION** Since Exit ID and Type are not checked on an update,
    // there is no need to squash the histories and/or paths
    
    //BPSquash(done_sn, block_pc, actually_exit_id, pred_exit_id, tid);
    //}

    BPUpdate(done_sn,
             block_pc,
             addr_space_id,
             actually_exit_id,
             pred_exit_id,
             tid);

    BTypeUpdate(done_sn,
                block_pc,
                addr_space_id,
                actually_exit_id,
                actually_branch_type,
                pred_branch_type,
                seq_mispredicted,
                tid,
                historyTracker.getPredicationPattern(done_sn, tid)
                );

//    if ((pred_exit_id != actually_exit_id) ||
//            (pred_branch_type != actually_branch_type)) {

    //historyTracker.squash(block_pc, done_sn, actually_exit_id,
            //actually_branch_type, tid);
//    }

    if (actually_branch_type == TheISA::seq) {
        // Nothing to do
    } else if (actually_branch_type == TheISA::branch) {

        if (btbType == NormalBTB) {

            defaultBTB.update(block_pc,
                              addr_space_id,
                              actually_exit_id,
                              corr_target);

        } else if (btbType == IndirectBTB) {

            uint64_t hist = getGlobalHistory(done_sn, tid);
            uint64_t path_hist = getPathHistory(done_sn, tid);

            iBTB.update(block_pc, hist, path_hist, corr_target);

        } else if (btbType == NewBTB) {

            // @ TODO : get real histories. 
            std::vector<uint64_t> hist;
            if (exitPredictor != Perf) {
                hist.resize(5);
                hist[BTB<Impl>::ExitHistory] = 
                    historyTracker.getExitHistory(done_sn, tid, actually_exit_id);
                hist[BTB<Impl>::TypeHistory] = 
                    historyTracker.getTypeHistory(done_sn, tid, actually_branch_type);
                hist[BTB<Impl>::BinaryHistory] = 
                    historyTracker.getBinaryHistory(done_sn, tid, actually_branch_type);
                hist[BTB<Impl>::PathHistory] = 
                    historyTracker.getPathHistory(done_sn, tid, block_pc);
                hist[BTB<Impl>::PredicationHistory] = 
                    historyTracker.getPredicationPattern(done_sn, tid);
            } else {
                hist.resize(5, 0);
            }

            newBTB->update(block_pc, hist, corr_target);

        } else {
            panic("Invalid btb type selected!\n");
        }

    } else if (actually_branch_type == TheISA::call) {

        unsigned index = CTB.CTBIdxGen(block_pc,
                                       addr_space_id,
                                       actually_exit_id);
        CTB.update(index, corr_target);

        RAS.lspush(block_pc, index, tid);

    } else if (actually_branch_type == TheISA::ret) {

        unsigned ctb_index = RAS.lsPopIdx(tid);

        Addr ctb_addr = RAS.lsPopAddr(tid);

        CTB.update(ctb_index, ctb_addr, corr_target);

    } else {
        panic("Unknown predicted branch type!");
    }
}

template<class Impl>
void
EdgeBPredUnit<Impl>::squash(const BlockID squashed_sn, ThreadID tid)
{    
    BPSquash(squashed_sn, tid);

    BTypeSquash(squashed_sn, tid);

    if (exitPredictor != Perf) {
        historyTracker.squash(squashed_sn, tid);
    }

    RAS.squash(squashed_sn, tid);
}

template <class Impl>
void
EdgeBPredUnit<Impl>::squash(const BlockID squashed_sn,
                            int addr_space_id,
                            Addr block_pc,
                            const Addr corr_target,
                            ExitType actually_branch_type,
                            ExitType pred_branch_type,
                            bool seq_mispredicted,
                            ExitID actually_exit_id,
                            ExitID pred_exit_id,
                            bool exit_mispredicted,
                            ThreadID tid)
{
    Addr retAddrPush = 0;

    DPRINTF(EdgePredUnit, "BranchPred: [tid:%i]: Squashing from sequence number %i, "
            "setting target to %#x. Real exit:%i\n",
            tid, squashed_sn, corr_target, actually_exit_id);
    
    // Only squash the exit predictor when exit is mispredicted.
//    if (exit_mispredicted) {
//        DPRINTF(EdgePredUnit, "BranchPred: [tid:%i]: Squashing the exit"
//                " predictor because exit id is mispredicted. "
//                "actual:%i, pred:%i\n",
//                tid, actually_exit_id, pred_exit_id);

    BPSquash(squashed_sn, 
             block_pc,
             actually_exit_id,
             pred_exit_id,
             tid);
//    }

    BPUpdate(squashed_sn,
             block_pc,
             addr_space_id,
             actually_exit_id,
             pred_exit_id,
             tid);

    RAS.squash(squashed_sn,tid);

    BTypeSquash(squashed_sn,
                block_pc,
                addr_space_id,
                actually_exit_id,
                actually_branch_type,
                pred_branch_type,
                seq_mispredicted,
                tid,
                historyTracker.getPredicationPattern(squashed_sn, tid)
                );

//    if ((pred_exit_id != actually_exit_id) ||
//            (pred_branch_type != actually_branch_type)) {

    if (exitPredictor != Perf) {
        historyTracker.squash(block_pc, squashed_sn, actually_exit_id,
                actually_branch_type, tid);
    }
//    }

    if (actually_branch_type == TheISA::call) {
        retAddrPush = CTB.lookupRetAddr(block_pc, addr_space_id, actually_exit_id);
    }

    if (actually_branch_type == TheISA::seq) {
        // Nothing to do
    } else if (actually_branch_type == TheISA::branch) {

        if (btbType == NormalBTB) {

            defaultBTB.update(block_pc,
                              addr_space_id,
                              actually_exit_id,
                              corr_target);

        } else if (btbType == IndirectBTB) {

            uint64_t hist = getGlobalHistory(squashed_sn, tid);
            uint64_t path_hist = getPathHistory(squashed_sn, tid);

            iBTB.update(block_pc, hist, path_hist, corr_target);

        } else if (btbType == NewBTB) {

            // @ TODO : now provide speculative histories to btb. 
            std::vector<uint64_t> hist;

            if (exitPredictor != Perf) {
                hist.resize(5);
                hist[BTB<Impl>::ExitHistory] = 
                    historyTracker.getExitHistory(tid);
                hist[BTB<Impl>::TypeHistory] = 
                    historyTracker.getTypeHistory(tid);
                hist[BTB<Impl>::BinaryHistory] = 
                    historyTracker.getBinaryHistory(tid);
                hist[BTB<Impl>::PathHistory] = 
                    historyTracker.getPathHistory(tid);
                hist[BTB<Impl>::PredicationHistory] = 
                    historyTracker.getPredicationPattern(squashed_sn, tid);
            } else {
                hist.resize(5, 0);
            }

            newBTB->update(block_pc, hist, corr_target);

        } else {
            panic("Invalid btb type selected!\n");
        }

    } else if (actually_branch_type == TheISA::call) {

        unsigned index = CTB.CTBIdxGen(block_pc,
                                       addr_space_id,
                                       actually_exit_id);

        CTB.update(index, corr_target);

        RAS.lspush(block_pc, index, tid);

        RAS.push(retAddrPush, tid);

        DPRINTF(EdgePredUnit,"Squashing!Exit Type is call. Return address is %#x.\n",
                retAddrPush<<7);

    } else if (actually_branch_type == TheISA::ret) {
        unsigned ctb_index = RAS.lsPopIdx(tid);

        Addr ctb_addr = RAS.lsPopAddr(tid);

        CTB.update(ctb_index, ctb_addr, corr_target);
        
        RAS.pop(tid);
    } else {
        panic("Unknown predicted branch type!");
    }
}

template <class Impl>
void
EdgeBPredUnit<Impl>::BPSquash(BlockID block_id, Addr block_pc, 
        ExitID actually_exit_id, ExitID pred_exit_id, ThreadID tid)
{
    if (exitPredictor == Perf) {
        return;
    } else if (exitPredictor == TargetOnly) {
        return;
    } else if (exitPredictor == Percep) {
        perceptronBP->squash(block_id, actually_exit_id, tid);
    } else if (exitPredictor == Tour) {
        tournamentBP->squash(block_id, actually_exit_id, tid);
    } else if (exitPredictor == TourPercep) {
        tournamentPerceptron->squash(block_id, actually_exit_id, tid);
    } else if (exitPredictor == Piecewise) {
        piecewiseExitP->squash(block_pc, block_id, actually_exit_id,
                pred_exit_id, tid);

    } else if (exitPredictor == Global) {
        globalExitBP->squash(block_id,
                             block_pc,
                             actually_exit_id,
                             tid);

    } else if (exitPredictor == Local) {
        localExitBP->squash(block_id,
                            block_pc,
                            actually_exit_id,
                            tid);

    } else if (exitPredictor == NewTour) {
        newTourExitBP->squash(block_id,
                              block_pc,
                              actually_exit_id,
                              tid);

    } else if (exitPredictor == TAGE) {
        tageExitBP->squash(block_id,
                           block_pc,
                           actually_exit_id,
                           tid);
    } else {
        panic("Invalid BP selected!");
    }
}

template<class Impl>
void
EdgeBPredUnit<Impl>::BTypeSquash(BlockID squashed_sn, Addr block_pc,
        int addr_space_id, ExitID actually_exit_id, ExitType actually_btype,
        ExitType pred_btype, bool seq_mispredicted, ThreadID tid,
        uint64_t predication_hist)
{
    if (btypePredictor == Perf || btypePredictor == PerfTypeOnly) {

        return;

    } else if (btypePredictor == Tour) {

//        if (actually_btype != pred_btype) {

        tournamentBtypeBP->squash(squashed_sn,
                                  block_pc,
                                  actually_btype,
                                  tid);
//        }

        tournamentBtypeBP->update(squashed_sn,
                                  block_pc,
                                  addr_space_id,
                                  actually_btype,
                                  tid);

    } else if (btypePredictor == NewTour) {

//        if (actually_btype != pred_btype) {

        newTourBtypeBP->squash(squashed_sn,
                               block_pc,
                               actually_btype,
                               tid);
//        }

        newTourBtypeBP->update(squashed_sn,
                               block_pc,
                               addr_space_id,
                               actually_btype,
                               tid);

    } else if (btypePredictor == Global) {

//        if (actually_btype != pred_btype) {

        globalBtypeBP->squash(squashed_sn,
                              block_pc,
                              actually_btype,
                              tid);
//        }

        globalBtypeBP->update(squashed_sn,
                              block_pc,
                              addr_space_id,
                              actually_btype,
                              tid);

    } else if (btypePredictor == Local) {

//        if (actually_btype != pred_btype) {

        localBtypeBP->squash(squashed_sn,
                             block_pc,
                             actually_btype,
                             tid);
//        }

        localBtypeBP->update(squashed_sn,
                             block_pc,
                             addr_space_id,
                             actually_btype,
                             tid);

    } else if (btypePredictor == TAGE) {

//        if (actually_btype != pred_btype) {

        tageBtypeBP->squash(squashed_sn,
                            block_pc,
                            actually_btype,
                            tid);
//        }

        tageBtypeBP->update(squashed_sn,
                            block_pc,
                            addr_space_id,
                            actually_btype,
                            tid);

    } else if (btypePredictor == PiecewiseSType) {

//        if (actually_btype != pred_btype) {
        piecewiseBStypeBP->squash(block_pc,
                                  squashed_sn,
                                  actually_btype,
                                  pred_btype,
                                  tid);
//        }

        piecewiseBStypeBP->update(squashed_sn,
                                  block_pc,
                                  actually_btype,
                                  pred_btype,
                                  tid);

    } else if (btypePredictor == Hysteresis) {

        hysteresisBtypeBP->update(block_pc,
                                  addr_space_id,
                                  actually_exit_id,
                                  0, // Leave path_hist to be blank
                                  0, // Leave hist to be blank
                                  predication_hist,
                                  actually_btype);     

    } else if (btypePredictor == Piecewise) {
        bool is_taken = (actually_btype == TheISA::seq) ? false : true;

//         Only squash the btype predictor when the branch type is
//         mispredicted.
        if (seq_mispredicted) {
            DPRINTF(EdgePredUnit, "BranchPred: [tid:%i]: Branch type is" 
                    " mispredicted, squashing the branch type predictor\n",
                    tid);

            piecewiseBtypeBP->squash(block_pc, 
                                     squashed_sn,
                                     is_taken,
                                     tid);
        }

        piecewiseBtypeBP->update(squashed_sn,
                                 block_pc,
                                 tid,
                                 is_taken,
                                 seq_mispredicted);

        uint32_t hist = getGlobalHistory(squashed_sn, tid);
        uint32_t path_hist = getPathHistory(squashed_sn, tid);

        DPRINTF(EdgePredUnit, "Get history %#x.\n", hist);
        DPRINTF(EdgePredUnit, "Get path history %#x.\n", path_hist);

        if (is_taken) {
            hysteresisBtypeBP->update(block_pc,
                                      addr_space_id,
                                      actually_exit_id,
                                      path_hist,
                                      hist,
                                      predication_hist,
                                      actually_btype);     
        }
    } else if (btypePredictor == Twostage) {

        twostageBtypeBP->squash(squashed_sn,
                                block_pc,
                                actually_btype,
                                addr_space_id,
                                tid,
                                seq_mispredicted);

    } else {
        panic("Invalid branch type predictor selected\n");
    }
}

template<class Impl>
void
EdgeBPredUnit<Impl>::BPSquash(BlockID block_id, ThreadID tid)
{
    if (exitPredictor == Perf) {
        return;
    } else if (exitPredictor == TargetOnly) {
        return;
    } else if(exitPredictor == Percep) {
        perceptronBP->squash(block_id, tid);
    } else if(exitPredictor == Tour) {
        tournamentBP->squash(block_id, tid);
    } else if(exitPredictor == TourPercep) {
        tournamentPerceptron->squash(block_id, tid);
    } else if (exitPredictor == Piecewise) {
        piecewiseExitP->squash(block_id, tid);
    } else if (exitPredictor == Global) {
        globalExitBP->squash(block_id, tid);
    } else if (exitPredictor == NewTour) {
        newTourExitBP->squash(block_id, tid);
    } else if (exitPredictor == Local) {
        localExitBP->squash(block_id, tid);
    } else if (exitPredictor == TAGE) {
        tageExitBP->squash(block_id, tid);
    } else {
        panic("Invalid BP selected!");
    }
}

template<class Impl>
void
EdgeBPredUnit<Impl>::BTypeSquash(BlockID squashed_sn, ThreadID tid)
{
    if (btypePredictor == Perf || btypePredictor == PerfTypeOnly) {
        return;
    } else if (btypePredictor == Piecewise) {
        piecewiseBtypeBP->squash(squashed_sn, tid);
    } else if (btypePredictor == PiecewiseSType) {
        piecewiseBStypeBP->squash(squashed_sn, tid);
    } else if (btypePredictor == Tour) {
        tournamentBtypeBP->squash(squashed_sn, tid);
    } else if (btypePredictor == NewTour) {
        newTourBtypeBP->squash(squashed_sn, tid);
    } else if (btypePredictor == Global) {
        globalBtypeBP->squash(squashed_sn, tid);
    } else if (btypePredictor == Local) {
        localBtypeBP->squash(squashed_sn, tid);
    } else if (btypePredictor == TAGE) {
        tageBtypeBP->squash(squashed_sn, tid);
    } else if (btypePredictor == Twostage) {
        twostageBtypeBP->squash(squashed_sn, tid);
    } else if (btypePredictor == Hysteresis) {
        return;
    } else {
        panic("Invalid BP selected!");
    }
}

template <class Impl>
TheISA::ExitID
EdgeBPredUnit<Impl>::BPLookup(BlockID block_id, BlockID oldest_block_id, 
        Addr block_pc, int addr_space_id, ThreadID tid)
{
    //If the exit exitPredictor is ignored, always return the exit id 0.
    if (exitPredictor == TargetOnly) {
        return 0;
    } else if (exitPredictor == Percep) {
        return perceptronBP->lookup(block_id, block_pc, tid);
    } else if (exitPredictor == Tour) {
        return tournamentBP->lookup(block_id, oldest_block_id,
                                    block_pc, addr_space_id, tid);
    } else if (exitPredictor == TourPercep) {
        return tournamentPerceptron->lookup(block_id, oldest_block_id,
                                            block_pc, addr_space_id, tid);
    } else if (exitPredictor == Piecewise) {
        return piecewiseExitP->lookup(block_id, oldest_block_id, block_pc,
                                      tid);
    } else if (exitPredictor == Global) {
        return globalExitBP->lookup(block_id,
                                    oldest_block_id,
                                    block_pc,
                                    addr_space_id,
                                    tid);
    } else if (exitPredictor == NewTour) {
        return newTourExitBP->lookup(block_id,
                                     oldest_block_id,
                                     block_pc,
                                     addr_space_id,
                                     tid);
    } else if (exitPredictor == Local) {
        return localExitBP->lookup(block_id,
                                   oldest_block_id,
                                   block_pc,
                                   addr_space_id,
                                   tid);
    } else if (exitPredictor == TAGE) {
        return tageExitBP->lookup(block_id,
                                  oldest_block_id,
                                  block_pc,
                                  addr_space_id,
                                  tid);
    } else {
        panic("Invalid BP selected!");
    }
}

template <class Impl>
TheISA::ExitType
EdgeBPredUnit<Impl>::BTypeLookup(BlockPtr & block, BlockID block_id,
        BlockID oldest_block_id, Addr block_pc, ExitID pred_exit_id,
        int addr_space_id, ThreadID tid, uint64_t predication_hist)
{
    ExitType pred_branch_type = TheISA::InvalidExitType;

    if (btypePredictor == Perf || btypePredictor == PerfTypeOnly) {

        return pred_branch_type;

    } else if (btypePredictor == Tour) {

        TheISA::BTypeID type_id =
            tournamentBtypeBP->lookup(block_id,
                                      oldest_block_id,
                                      block_pc,
                                      addr_space_id,
                                      tid);
        pred_branch_type = static_cast<TheISA::ExitType>(type_id);

    } else if (btypePredictor == NewTour) {
        TheISA::BTypeID type_id =
            newTourBtypeBP->lookup(block_id,
                                   oldest_block_id,
                                   block_pc,
                                   addr_space_id,
                                   tid);
        pred_branch_type = static_cast<TheISA::ExitType>(type_id);

    } else if (btypePredictor == Global) {
        TheISA::BTypeID type_id =
            globalBtypeBP->lookup(block_id,
                                  oldest_block_id,
                                  block_pc,
                                  addr_space_id,
                                  tid);
        pred_branch_type = static_cast<TheISA::ExitType>(type_id);

    } else if (btypePredictor == Local) {
        TheISA::BTypeID type_id =
            localBtypeBP->lookup(block_id,
                                 oldest_block_id,
                                 block_pc,
                                 addr_space_id,
                                 tid);
        pred_branch_type = static_cast<TheISA::ExitType>(type_id);

    } else if (btypePredictor == TAGE) {

        TheISA::BTypeID type_id =
            tageBtypeBP->lookup(block_id,
                                oldest_block_id,
                                block_pc,
                                addr_space_id,
                                tid);

        pred_branch_type = static_cast<TheISA::ExitType>(type_id);

    } else if (btypePredictor == PiecewiseSType) {

        TheISA::BTypeID type_id = 
            piecewiseBStypeBP->lookup(block_id,
                                      oldest_block_id,
                                      block_pc,
                                      tid);
        pred_branch_type = static_cast<TheISA::ExitType>(type_id);

    } else if (btypePredictor == Hysteresis) {

        pred_branch_type =
            hysteresisBtypeBP->lookup(block_pc,
                                      addr_space_id, 
                                      pred_exit_id,
                                      0, // Leave path_hist to blank
                                      0, // Leave hist to blank
                                      predication_hist);

    } else if (btypePredictor == Piecewise) {

        bool is_taken = piecewiseBtypeBP->lookup(block_id,
                                                 oldest_block_id,
                                                 block_pc,
                                                 tid);

        uint32_t hist = getGlobalHistory(block_id, tid);
        uint32_t path_hist = getPathHistory(block_id, tid);

        DPRINTF(EdgePredUnit, "Get history %#x.\n", hist);
        DPRINTF(EdgePredUnit, "Get path history %#x.\n", path_hist);

        if (is_taken) {
            pred_branch_type =
                hysteresisBtypeBP->lookup(block_pc,
                                          addr_space_id, 
                                          pred_exit_id,
                                          path_hist,
                                          hist,
                                          predication_hist);

            DPRINTF(EdgePredUnit, "Non-seqential target.\n");

        } else {
            pred_branch_type = TheISA::seq;

            DPRINTF(EdgePredUnit, "Sequential target.\n");
        }
    } else if (btypePredictor == Twostage) {

        TheISA::BTypeID type_id =
            twostageBtypeBP->lookup(block,
                                    block_id,
                                    oldest_block_id,
                                    block_pc,
                                    addr_space_id,
                                    tid);

        pred_branch_type = static_cast<TheISA::ExitType>(type_id);

    } else {
        panic("Invalid branch type predictor selected\n");
    }

    return pred_branch_type;
}

template <class Impl>
void
EdgeBPredUnit<Impl>::BPUpdate(BlockID block_id, Addr block_pc, 
        int addr_space_id, ExitID actually_exit_id, ExitID pred_exit_id,
        ThreadID tid)
{
    if (exitPredictor == Perf) {
        return;
    } else if (exitPredictor == TargetOnly) {
        return;
    } else if (exitPredictor == Percep) {
        perceptronBP->update(block_id, block_pc, actually_exit_id);
    } else if (exitPredictor == Tour) {
        tournamentBP->update(block_id, block_pc, addr_space_id, actually_exit_id,
                tid);
    } else if (exitPredictor == TourPercep) {
        tournamentPerceptron->update(block_id,block_pc,addr_space_id,
                actually_exit_id, tid);
    } else if (exitPredictor == Piecewise) {
        piecewiseExitP->update(block_id, block_pc, actually_exit_id,
                               pred_exit_id, tid);
    } else if (exitPredictor == Global) {
        globalExitBP->update(block_id,
                             block_pc,
                             addr_space_id,
                             actually_exit_id,
                             tid);

    } else if (exitPredictor == NewTour) {
        newTourExitBP->update(block_id,
                              block_pc,
                              addr_space_id,
                              actually_exit_id,
                              tid);

    } else if (exitPredictor == Local) {
        localExitBP->update(block_id,
                            block_pc,
                            addr_space_id,
                            actually_exit_id,
                            tid);

    } else if (exitPredictor == TAGE) {
        tageExitBP->update(block_id,
                           block_pc,
                           addr_space_id,
                           actually_exit_id,
                           tid);
    } else {
        panic("Invalid BP selected!");
    }
}

template <class Impl>
void
EdgeBPredUnit<Impl>::BTypeUpdate(BlockID done_sn, Addr block_pc,
        int addr_space_id, ExitID actually_exit_id, ExitType actually_btype,
        ExitType pred_btype, bool seq_mispredicted, ThreadID tid, uint64_t predication_hist)
{
    // **ATTENTION** Since Exit ID and Type are not checked on an update,
    // there is no need to squash the histories and/or paths
    if (btypePredictor == Perf || btypePredictor == PerfTypeOnly) {

        return;

    } if (btypePredictor == Tour) {

//        if (actually_btype != pred_btype) {

//        tournamentBtypeBP->squash(done_sn,
//                                  block_pc,
//                                  actually_btype,
//                                  tid);
//        }

        tournamentBtypeBP->update(done_sn,
                                  block_pc,
                                  addr_space_id,
                                  actually_btype,
                                  tid);

    } else if (btypePredictor == NewTour) {

//        if (actually_btype != pred_btype) {

//        newTourBtypeBP->squash(done_sn,
//                               block_pc,
//                               actually_btype,
//                               tid);
//        }

        newTourBtypeBP->update(done_sn,
                               block_pc,
                               addr_space_id,
                               actually_btype,
                               tid);

    } else if (btypePredictor == Global) {

//        if (actually_btype != pred_btype) {

//        globalBtypeBP->squash(done_sn,
//                               block_pc,
//                               actually_btype,
//                               tid);
//        }

        globalBtypeBP->update(done_sn,
                              block_pc,
                              addr_space_id,
                              actually_btype,
                              tid);

    } else if (btypePredictor == Local) {

//        if (actually_btype != pred_btype) {

//        localBtypeBP->squash(done_sn,
//                             block_pc,
//                             actually_btype,
//                             tid);
//        }

        localBtypeBP->update(done_sn,
                             block_pc,
                             addr_space_id,
                             actually_btype,
                             tid);

    } else if (btypePredictor == TAGE) {

//        if (actually_btype != pred_btype) {

//        tageBtypeBP->squash(done_sn,
//                            block_pc,
//                            actually_btype,
//                            tid);
//        }

        tageBtypeBP->update(done_sn,
                            block_pc,
                            addr_space_id,
                            actually_btype,
                            tid);

    } else if (btypePredictor == PiecewiseSType) {

//        if (actually_btype != pred_btype) {
//        piecewiseBStypeBP->squash(block_pc,
//                                  done_sn,
//                                  actually_btype,
//                                  pred_btype,
//                                  tid);
//        }

        piecewiseBStypeBP->update(done_sn,
                                  block_pc,
                                  actually_btype,
                                  pred_btype,
                                  tid);

    } else if (btypePredictor == Hysteresis) {

        hysteresisBtypeBP->update(block_pc,
                                  addr_space_id,
                                  actually_exit_id,
                                  0, // Leave path_hist to blank
                                  0, // Leave hist to blank
                                  predication_hist,
                                  actually_btype);

    } else if (btypePredictor == Piecewise) {

//         Is this a sequential target block (not taken) or not?
        bool is_taken = (actually_btype == TheISA::seq) ? false : true;

//         Only squash the btype predictor when the branch type is
//         mispredicted.
//        if (seq_mispredicted) {
//            DPRINTF(EdgePredUnit, "BranchPred: [tid:%i]: Branch type is" 
//                    " mispredicted, squashing the branch type predictor\n",
//                    tid);

//            piecewiseBtypeBP->squash(block_pc, 
//                                     done_sn,
//                                     is_taken,
//                                     tid);
//        }

        piecewiseBtypeBP->update(done_sn,
                                 block_pc, 
                                 tid,
                                 is_taken,
                                 seq_mispredicted);

        uint32_t hist = getGlobalHistory(done_sn, tid);
        uint32_t path_hist = getPathHistory(done_sn, tid);

        DPRINTF(EdgePredUnit, "Get history %#x.\n", hist);
        DPRINTF(EdgePredUnit, "Get path history %#x.\n", path_hist);

        if (is_taken) {
            hysteresisBtypeBP->update(block_pc,
                                      addr_space_id,
                                      actually_exit_id,
                                      path_hist,
                                      hist,
                                      predication_hist,
                                      actually_btype);
        }
    } else if (btypePredictor == Twostage) {

        twostageBtypeBP->update(done_sn,
                                block_pc,
                                addr_space_id,
                                actually_btype,
                                tid,
                                seq_mispredicted);

    } else {
        panic("Invalid branch type predictor selected!\n");
    }
}

template<class Impl>
uint64_t
EdgeBPredUnit<Impl>::getGlobalHistory(BlockID block_id, ThreadID tid)
{
    if (btypePredictor == Piecewise) {
        return piecewiseBtypeBP->getGlobalHistory(block_id, tid);
    } else if (btypePredictor == PiecewiseSType) {
        return piecewiseBStypeBP->getGlobalHistory(block_id, tid);
    } else {
        panic("Invalid BType Predictor selected!");
    }
}

template<class Impl>
uint64_t
EdgeBPredUnit<Impl>::getPathHistory(BlockID block_id, ThreadID tid)
{
    if (btypePredictor == Piecewise) {
        return piecewiseBtypeBP->getPathHistory(block_id, tid);
    } else if (btypePredictor == PiecewiseSType) {
        return piecewiseBStypeBP->getPathHistory(block_id, tid);
    } else {
        panic("Invalid BType Predictor selected!");
    }
}

template <class Impl>
void
EdgeBPredUnit<Impl>::dump()
{
    panic("Unimplemented func: dump()\n");
}

